package com.zh.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zh.common.exception.BusinessException;
import com.zh.common.utils.RedisUtil;
import com.zh.system.domain.dto.AddDicTypeDto;
import com.zh.system.domain.dto.UpdateDicTypeDto;
import com.zh.system.domain.entity.SysDicType;
import com.zh.system.domain.vo.SysValVo;
import com.zh.system.domain.vo.SysDicValTreeVo;
import com.zh.system.domain.tmp.SysDicTmp;
import com.zh.system.mapper.SysDicTypeMapper;
import com.zh.system.mapper.SysDicValueMapper;
import com.zh.system.service.SysDicService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
@DS("ls_system")
public class SysDicServiceImpl extends ServiceImpl<SysDicTypeMapper, SysDicType> implements SysDicService {

    private final static String DIC_KEY_PREFIX = "ls:system:dic:";
    private final static String VALUE_KEY_PREFIX = "ls:system:value:";
    private final SysDicTypeMapper sysDicTypeMapper;
    private final SysDicValueMapper sysDicValueMapper;
    private final RedisUtil redisUtil;

    /**
     * 获取所有字典
     *
     * @return List<SysDicValTreeVo> 所有字典
     */
    @Override
    public List<SysDicValTreeVo> getDicTreeByType() {
        return getDicTreeByType(null);
    }

    /**
     * 获取字典树
     *
     * @param type 根字典类型
     * @return SysDicTreeVo 树形字典
     */
    @Override
    public List<SysDicValTreeVo> getDicTreeByType(String type) {
        // 从缓存中获取
        String key = DIC_KEY_PREFIX + Objects.requireNonNullElse(type, "total");
        List<SysDicValTreeVo> treeVoList = redisUtil.getList(key, SysDicValTreeVo.class);
        if (ObjectUtil.isNotEmpty(treeVoList)) {
            return treeVoList;
        }
        //单根节点判断
        if (type != null) {
            // 查找是否存在这个字典类型
            LambdaQueryWrapper<SysDicType> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(SysDicType::getDicType, type);
            Integer i = sysDicTypeMapper.selectCount(wrapper);
            if (i < 1) {
                throw new BusinessException("字典类型[" + type + "]不存在！");
            }
        }
        //查询所有字典类型
        CompletableFuture<List<SysDicTmp>> task1 = CompletableFuture.supplyAsync(() -> {
            String tKey = DIC_KEY_PREFIX + "all";
            List<SysDicType> typeList = redisUtil.getList(tKey, SysDicType.class);
            if (CollUtil.isEmpty(typeList)) {
                typeList = sysDicTypeMapper.selectAll();
                redisUtil.setList(tKey, typeList, 31, TimeUnit.DAYS);
            }
            List<SysDicTmp> sysDicTmpList = typeList
                    .stream()
                    .map(dicType -> BeanUtil.copyProperties(dicType, SysDicTmp.class))
                    .collect(Collectors.toList());
            return sysDicTmpList;
        });
        // 查找所有字典值
        CompletableFuture<List<SysValVo>> task2 = task1.thenApplyAsync((sysDicTmpList) -> {
            String vKey = VALUE_KEY_PREFIX + "all";
            List<SysValVo> valueList = redisUtil.getList(vKey, SysValVo.class);
            if (CollUtil.isEmpty(valueList)) {
                valueList = sysDicValueMapper.selectAll();
                redisUtil.setList(vKey, valueList, 7, TimeUnit.DAYS);
            }
            return valueList;
        });
        // 等待两个任务都完成
        CompletableFuture.allOf(task1, task2).join();
        List<SysDicTmp> sysDicTmpList = task1.getNow(new ArrayList<>());
        List<SysValVo> valueList = task2.getNow(new ArrayList<>());
        // 构建树
        if (type != null) {
            //单根节点树
            treeVoList = buildTree(sysDicTmpList, type, valueList);
        } else {
            //全根节点树
            treeVoList = buildTree(sysDicTmpList, valueList);
        }
        if (ObjectUtil.isNotEmpty((treeVoList))) {
            redisUtil.setList(key, treeVoList, 7, TimeUnit.DAYS);
        }
        return treeVoList;
    }

    /**
     * 构建单节点树
     *
     * @param sysDicTmpList  所有字典类型
     * @param type           根字典类型
     * @param finalValueList 所有字典值
     * @return List<SysDicValTreeVo> 树形字典
     */
    private List<SysDicValTreeVo> buildTree(List<SysDicTmp> sysDicTmpList, String type, List<SysValVo> finalValueList) {
        return sysDicTmpList.stream().filter(dicTmp -> dicTmp.getDicType().equals(type)).map(
                (dicTmp) -> {
                    SysDicValTreeVo treeVo = new SysDicValTreeVo();
                    List<SysValVo> values = finalValueList.stream()
                            .filter(value -> value.getDicType().equals(dicTmp.getDicType()))
                            .sorted(Comparator.comparing(SysValVo::getDicSort, Comparator.naturalOrder()))
                            .collect(Collectors.toList());
                    treeVo.setId(dicTmp.getId());
                    treeVo.setDicType(dicTmp.getDicType());
                    treeVo.setDicName(dicTmp.getDicName());
                    treeVo.setValues(values);
                    treeVo.setDicSort(dicTmp.getDicSort());
                    treeVo.setChildren(getChildren(dicTmp, sysDicTmpList, finalValueList));
                    treeVo.getChildren().sort(Comparator.comparing(SysDicValTreeVo::getDicSort, Comparator.naturalOrder()));
                    return treeVo;
                }
        ).collect(Collectors.toList());
    }

    /**
     * 构建全节点树
     *
     * @param sysDicTmpList  所有字典类型
     * @param finalValueList 所有字典值
     * @return List<SysDicValTreeVo> 树形字典
     */
    private List<SysDicValTreeVo> buildTree(List<SysDicTmp> sysDicTmpList, List<SysValVo> finalValueList) {
        return sysDicTmpList.stream().filter(dicTmp -> 0 == dicTmp.getPId()).map(
                (dicTmp) -> {
                    SysDicValTreeVo treeVo = new SysDicValTreeVo();
                    List<SysValVo> values = finalValueList.stream()
                            .filter(value -> value.getDicType().equals(dicTmp.getDicType()))
                            .sorted(Comparator.comparing(SysValVo::getDicSort, Comparator.naturalOrder()))
                            .collect(Collectors.toList());
                    treeVo.setId(dicTmp.getId());
                    treeVo.setDicType(dicTmp.getDicType());
                    treeVo.setDicName(dicTmp.getDicName());
                    treeVo.setDicSort(dicTmp.getDicSort());
                    treeVo.setValues(values);
                    treeVo.setChildren(getChildren(dicTmp, sysDicTmpList, finalValueList));
                    return treeVo;
                }
        ).collect(Collectors.toList());
    }

    /**
     * 递归查询子节点
     *
     * @param root 根节点
     * @param all  所有节点
     * @return 根节点信息
     */
    private List<SysDicValTreeVo> getChildren(SysDicTmp root, List<SysDicTmp> all, List<SysValVo> finalValueList) {
        List<SysDicValTreeVo> collect = all.stream().filter(dicTmp -> Objects.equals(dicTmp.getPId(), root.getId())).map(
                (dicTmp) -> {
                    SysDicValTreeVo treeVo = new SysDicValTreeVo();
                    List<SysValVo> values = finalValueList.stream()
                            .filter(value -> value.getDicType().equals(dicTmp.getDicType()))
                            .sorted(Comparator.comparing(SysValVo::getDicSort, Comparator.naturalOrder()))
                            .collect(Collectors.toList());
                    treeVo.setId(dicTmp.getId());
                    treeVo.setDicType(dicTmp.getDicType());
                    treeVo.setDicName(dicTmp.getDicName());
                    treeVo.setValues(values);
                    treeVo.setDicSort(dicTmp.getDicSort());
                    treeVo.setChildren(getChildren(dicTmp, all, finalValueList));
                    treeVo.getChildren().sort(Comparator.comparing(SysDicValTreeVo::getDicSort, Comparator.naturalOrder()));
                    return treeVo;
                }
        ).collect(Collectors.toList());
        return collect;
    }

    /**
     * 添加字典类型
     *
     * @param addDicTypeDto 字典类型信息
     * @return boolean
     */
    @Override
    public Boolean addDicType(AddDicTypeDto addDicTypeDto) {
        Optional.ofNullable(addDicTypeDto.getDicType()).orElseThrow(() -> new BusinessException("字典类型不能为空"));
        Optional.ofNullable(addDicTypeDto.getDicName()).orElseThrow(() -> new BusinessException("字典名称不能为空"));
        SysDicType sysDicType = BeanUtil.copyProperties(addDicTypeDto, SysDicType.class);
        LambdaQueryWrapper<SysDicType> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysDicType::getDicType, sysDicType.getDicType());
        Integer count = sysDicTypeMapper.selectCount(wrapper);
        if (count > 0) {
            throw new BusinessException("字典类型[" + sysDicType.getDicType() + " ]已存在");
        }
        // 删除缓存
        deleteCache();
        return sysDicTypeMapper.insert(sysDicType) > 0;
    }

    /**
     * 更新字典类型状态
     *
     * @param updateDicTypeDto 字典类型信息
     * @return boolean
     */
    @Override
    @Transactional
    public Boolean updateDicType(UpdateDicTypeDto updateDicTypeDto) {
        SysDicType sysDicType = BeanUtil.copyProperties(updateDicTypeDto, SysDicType.class);
        int i = sysDicTypeMapper.updateById(sysDicType);
        // 删除缓存
        deleteCache();
        return i > 0;
    }


    /**
     * 删除字典类型
     *
     * @param id 字典类型id
     * @return boolean
     */
    @Override
    @Transactional
    public String deleteDicType(Long id) {
        //1.本字典类型下还有无子字典
        Integer count = sysDicTypeMapper.selectSubTypeCount(id);
        if (count > 0) {
            throw new BusinessException("该字典类型下还有子字典，无法删除！");
        }
        //2.若删除本字典，该字典下所有字典值都将删除
        sysDicTypeMapper.deleteById(id);
        sysDicValueMapper.deleteDicValueByType(id);
        deleteAllExpireDic();
        //3. 删除缓存
        deleteCache();
        return "删除成功";
    }

    @Override
    public List<SysDicType> selectRootTypeList() {
        LambdaQueryWrapper<SysDicType> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysDicType::getPId, 0);
        wrapper.orderBy(true, true, SysDicType::getDicSort);
        return sysDicTypeMapper.selectList(wrapper);
    }

    /**
     * todo删除过期字典
     */
    @Override
    @Transactional
    public void deleteAllExpireDic() {
        sysDicTypeMapper.permanentlyDelete();
        sysDicValueMapper.permanentlyDelete();
    }


    /**
     * 清除缓存
     */
    public void deleteCache() {
        redisUtil.deleteByPrefix(DIC_KEY_PREFIX);
        redisUtil.deleteByPrefix(VALUE_KEY_PREFIX);
    }

}
